/*  Copyright (c) 2012 Xiaoyun Zhu
 * 
 *  Permission is hereby granted, free of charge, to any person obtaining a copy  
 *  of this software and associated documentation files (the "Software"), to deal  
 *  in the Software without restriction, including without limitation the rights  
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell  
 *  copies of the Software, and to permit persons to whom the Software is  
 *  furnished to do so, subject to the following conditions:
 *  
 *  The above copyright notice and this permission notice shall be included in  
 *  all copies or substantial portions of the Software.
 *  
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR  
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,  
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE  
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER  
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,  
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN  
 *  THE SOFTWARE.  
 */
package cn.kk.fluidcells.api;

import java.util.StringTokenizer;

public class CellIdentifier {
    private final int major;

    private final int minor;

    private final int revision;

    private final String name;

    public CellIdentifier(String name, int major, int minor, int revision) {
        this.name = name;
        this.major = major;
        this.minor = minor;
        this.revision = revision;
    }

    public CellIdentifier(final String identifierString) {
        int idx;
        int majorVersion = -1;
        int minorVersion = -1;
        int revisionVersion = -1;
        String nameString;
        if ((idx = identifierString.indexOf(';')) == -1) {
            nameString = identifierString;
        } else {
            nameString = identifierString.substring(0, idx);
            final String versionString = identifierString.substring(idx + 1);
            StringTokenizer versions = new StringTokenizer(versionString, ";");
            if (-1 != (majorVersion = getIntValue(versions.nextToken()))) {
                if (-1 != (minorVersion = getIntValue(versions.nextToken()))) {
                    revisionVersion = getIntValue(versions.nextToken());
                }
            }
        }
        this.major = majorVersion;
        this.minor = minorVersion;
        this.revision = revisionVersion;
        this.name = nameString;
    }

    private int getIntValue(String nextToken) {
        if (nextToken != null) {
            try {
                return Integer.parseInt(nextToken);
            } catch (NumberFormatException e) {
                // ignore
            }
        }
        return -1;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(this.name);
        if (this.major != -1) {
            sb.append(';').append(this.major);
            if (this.minor != -1) {
                sb.append('.').append(this.minor);
                if (this.revision != -1) {
                    sb.append('.').append(this.revision);
                }
            }
        }
        return sb.toString();
    }

    /**
     * 
     * @param dependency
     *            cell identifier of dependency (minimum requirement)
     * @return true if both have identical names and major versions and minor and revision of cell identifier are newer
     *         than those of the dependency. If major, minor or revision are not set (-1), then they are interpreted as
     *         compatible.
     */
    public boolean isCompatibleWith(CellIdentifier dependency) {
        if (isSame(dependency)) {
            if ((this.major == -1) || (dependency.major == -1)) {
                return true;
            } else {
                // major must be identical
                if (this.major == dependency.major) {
                    if ((this.minor == -1) || (dependency.minor == -1) || (this.minor > dependency.minor)) {
                        return true;
                    } else if (this.minor == dependency.minor) {
                        if ((this.revision == -1) || (dependency.revision == -1)
                                || (this.revision >= dependency.revision)) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    public boolean isSame(CellIdentifier other) {
        return this.name.equals(other.name);
    }

    public boolean isNewerThan(CellIdentifier other) {
        if (isSame(other)) {
            if ((other.major == -1) || (this.major > other.major)) {
                return true;
            } else if (this.major < other.major) {
                return false;
            }
            if ((other.minor == -1) || (this.minor > other.minor)) {
                return true;
            } else if (this.minor < other.minor) {
                return false;
            }
            if ((other.revision == -1) || (this.revision > other.revision)) {
                return true;
            }
        }
        return false;
    }

    /**
     * @return the major
     */
    public int getMajor() {
        return this.major;
    }

    /**
     * @return the minor
     */
    public int getMinor() {
        return this.minor;
    }

    /**
     * @return the revision
     */
    public int getRevision() {
        return this.revision;
    }

    /**
     * @return the name
     */
    public String getName() {
        return this.name;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = (prime * result) + this.major;
        result = (prime * result) + this.minor;
        result = (prime * result) + ((this.name == null) ? 0 : this.name.hashCode());
        result = (prime * result) + this.revision;
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if ((obj == null) || (getClass() != obj.getClass())) {
            return false;
        }
        CellIdentifier other = (CellIdentifier) obj;
        if (isSame(other) && (this.major == other.major) && (this.minor == other.minor)
                && (this.revision == other.revision)) {
            return true;
        }
        return false;
    }
}
